#include "utilities.h"

//This include the status request/response processing
void statusProcessing(msg_header msgRespHdr, listBuffStruct eventReq)
{
	map<int,writeListData>::iterator it;
	list<statusSTR>::iterator itR;
	list<fromUOIDStr>::iterator itF;
	//Process the status request accordingly
	if((uint8_t)msgRespHdr.msgtype == 0xAC)
	{
		status_msg tempMsg;
		memcpy(&tempMsg.statustype, &(eventReq.listBuff[27]), sizeof(uint8_t));
		if(printFlag==1)
		{
			cout<<"Got Status Request msg from - "<<eventReq.portNumber<<endl;
		}
		//Find the UOID in the uoid queue and remove if duplicate i.e. if testUOID = 1
		int testUOID = findInUOIDList(msgRespHdr.uoid);
		if(testUOID == 1)
		{
			
		}
		else
		{
			//Put the UOID in uoid queue
			uoidStr tempUOIDStr;
			struct timeval time1;
			gettimeofday(&time1, NULL);
			tempUOIDStr.timeOfTheDay = (double)(time1.tv_sec * (1e+3) + (double)time1.tv_usec/1000);
			tempUOIDStr.msgLifeTime = initMap["MsgLifetime"];
			memcpy(tempUOIDStr.msgUOID, msgRespHdr.uoid, 20);
			pthread_mutex_lock(&uoidlock);
			myUOIDList.push_back(tempUOIDStr);
			pthread_mutex_unlock(&uoidlock);
			//if TTL is 1 then do not forward
			if(msgRespHdr.timetolive==1)
			{
				
			}
			else
			{
				//Forward the request to neighbors and reduce TTL by 1
				listBuffStruct lbtemp;
				lbtemp.logtype=2;
				int tempTTL = msgRespHdr.timetolive;
				tempTTL--;
				msgRespHdr.timetolive = tempTTL;
				memset(&lbtemp.listBuff, '\0', 27+sizeof(tempMsg));
				fillMsgHdrBuffer(msgRespHdr, lbtemp.listBuff);
				memcpy(&(lbtemp.listBuff[27]),&tempMsg.statustype, sizeof(uint8_t));
				lbtemp.datalength = 27 + sizeof(uint8_t);
				pthread_mutex_lock(&writeMapMutex);
				for(it=writeMap.begin(); it!=writeMap.end(); it++)
				{
					if((*it).first!=eventReq.portNumber)
					{
						//store the uoid in a map so that when it recieves a response then send it to the persone who initiated it.
						fromUOIDStr fromTemp;
						memcpy(fromTemp.uoid, &msgRespHdr.uoid, 20);
						fromTemp.portnumber = eventReq.portNumber;
						myFromUOIDList.push_back(fromTemp);
						lbtemp.portNumber = (*it).first;
						writeListData listDataStr = writeMap[(*it).first];
						listDataStr.response.push_back(lbtemp);
						writeMap[(*it).first] = listDataStr;
					}
				}
				pthread_mutex_unlock(&writeMapMutex);
			}
			
			//Check if the status request type is files or neighbors
			if(tempMsg.statustype == 0x01)
			{
				//Make the Response and put in write queue
				//1. Make status response
				int dataLen = 0;
				status_response_msg statusRspMsg;
				memcpy(&statusRspMsg.uoid, &msgRespHdr.uoid, 20);
				memset(&statusRspMsg.hostname, '\0', 512);
				memcpy(&statusRspMsg.hostname, hostname, strlen(hostname));
				statusRspMsg.hostport = port_recieve;
				statusRspMsg.hostinfolength = strlen(hostname) + sizeof(uint16_t);
				dataLen = strlen(hostname)+(2*sizeof(uint16_t))+20;
				int tempLeng = 0;
				memset(&statusRspMsg.data, '\0', 8192);
				int tempLastStrlen = 0;
				pthread_mutex_lock(&writeMapMutex);
				for(it=writeMap.begin(); it!=writeMap.end(); it++)
				{
					uint32_t len = sizeof(uint16_t) + strlen((*it).second.hostname);
					memcpy(&(statusRspMsg.data[tempLeng]), &len, sizeof(uint32_t));
					uint16_t port = (*it).first;
					memcpy(&(statusRspMsg.data[tempLeng + sizeof(uint32_t)]), &port, sizeof(uint16_t));
					memcpy(&(statusRspMsg.data[tempLeng + sizeof(uint32_t) + sizeof(uint16_t)]), (*it).second.hostname, strlen((*it).second.hostname));
					if(printFlag==1)
					{
						printf("%s\n",&(statusRspMsg.data[tempLeng + sizeof(uint32_t) + sizeof(uint16_t)]));
					}
					tempLeng = tempLeng + sizeof(uint16_t) + sizeof(uint32_t) + strlen((*it).second.hostname);
					tempLastStrlen = sizeof(uint32_t) + sizeof(uint16_t) + strlen((*it).second.hostname);
					if(printFlag==1)
					{
						cout<<"tmepLeng "<<tempLeng<<endl;
					}
				}
				pthread_mutex_unlock(&writeMapMutex);
			
				uint32_t lastZero = 0;
				dataLen = dataLen + tempLeng;
				memcpy(&(statusRspMsg.data[tempLeng-tempLastStrlen]), &lastZero, sizeof(uint32_t));
				//2. Make header
				char obj[512];
				msg_header responseHdr;
				strcpy(obj,"STRS");
				createMsgHdrStr(&responseHdr,0xAB,initMap["TTL"].asDouble,obj,node_instance_id, dataLen);
				
				//copy the above structures into message response structure
				listBuffStruct lbtemp;
				lbtemp.logtype=1;
				memset(&lbtemp.listBuff, '\0', 27+dataLen);
				fillMsgHdrBuffer(responseHdr, lbtemp.listBuff);

				memcpy(&(lbtemp.listBuff[27]),&(statusRspMsg.uoid),20);
				memcpy(&(lbtemp.listBuff[47]),&(statusRspMsg.hostinfolength),sizeof(uint16_t));
				memcpy(&(lbtemp.listBuff[49]),&(statusRspMsg.hostport),sizeof(uint16_t));
				memcpy(&(lbtemp.listBuff[51]),statusRspMsg.hostname,statusRspMsg.hostinfolength-sizeof(uint16_t));
				memcpy(&(lbtemp.listBuff[51+statusRspMsg.hostinfolength-sizeof(uint16_t)]),statusRspMsg.data,dataLen - 24 - statusRspMsg.hostinfolength + sizeof(uint16_t));
				lbtemp.datalength = 27+dataLen;
				lbtemp.portNumber = eventReq.portNumber;
				pthread_mutex_lock(&writeMapMutex);
				writeListData listDataStr = writeMap[eventReq.portNumber];
				//put them in the writeQueue.
				listDataStr.response.push_back(lbtemp);
				writeMap[eventReq.portNumber] = listDataStr;
				pthread_mutex_unlock(&writeMapMutex);
			}
			else if(tempMsg.statustype == 0x02)
			{
				list<storeListStr>::iterator itMyStoreList;
				
				//Make the Response and put in write queue
				//1. Make status responseHdr
				int dataLen = 0;
				status_response_msg statusRspMsg;
				memcpy(&statusRspMsg.uoid, &msgRespHdr.uoid, 20);
				memset(&statusRspMsg.hostname, '\0', 512);
				memcpy(&statusRspMsg.hostname, hostname, strlen(hostname));
				statusRspMsg.hostport = port_recieve;
				statusRspMsg.hostinfolength = strlen(hostname) + sizeof(uint16_t);
				dataLen = strlen(hostname)+(2*sizeof(uint16_t))+20;
				int tempLeng = 0;
				memset(&statusRspMsg.data, '\0', 8192);
				int tempLastStrlen = 0;
				int ksz = 0;
				uint32_t fileLen = 0;
				pthread_mutex_lock(&storelock);
				for(itMyStoreList = myStoreList.begin(); itMyStoreList != myStoreList.end(); itMyStoreList++)
				{
					fileLen = 0;
					char filename[128];
					char fileData[512];
					memset(fileData, '\0', 512);
					memset(filename, '\0', 128);
					strcpy(filename,initMap["HomeDir"].asStr);
					strcat(filename,"/");
					strcat(filename,"files/");
					char ind[10];
					memset(ind, '\0', 10);
					sprintf(ind, "%d", itMyStoreList->fileIndex);
					strcat(filename, ind);
					strcat(filename, ".meta");
					struct stat st;
					ksz = stat(filename, &st);
					fileLen = (uint32_t)st.st_size;
					memcpy(&(statusRspMsg.data[tempLeng]), &fileLen, sizeof(uint32_t));
					FILE *fileP;
					fileP = fopen(filename, "r");
					fread(fileData, 1, fileLen, fileP);
					fclose(fileP);
					memcpy(&(statusRspMsg.data[tempLeng+sizeof(uint32_t)]), &fileData, fileLen);
					tempLeng = tempLeng + sizeof(uint32_t) + fileLen;
					tempLastStrlen = sizeof(uint32_t) + fileLen;
				}
				pthread_mutex_unlock(&storelock);
				
				uint32_t lastZero = 0;
				dataLen = dataLen + tempLeng;
				memcpy(&(statusRspMsg.data[tempLeng-tempLastStrlen]), &lastZero, sizeof(uint32_t));
				
				//2. Make header
				char obj[512];
				msg_header responseHdr;
				strcpy(obj,"STRS");
				createMsgHdrStr(&responseHdr,0xAB,initMap["TTL"].asDouble,obj,node_instance_id, dataLen);
				
				//copy the above structures into message response structure
				listBuffStruct lbtemp;
				lbtemp.logtype=1;
				memset(&lbtemp.listBuff, '\0', 8192);
				fillMsgHdrBuffer(responseHdr, lbtemp.listBuff);
				
				memcpy(&(lbtemp.listBuff[27]),&(statusRspMsg.uoid),20);
				memcpy(&(lbtemp.listBuff[47]),&(statusRspMsg.hostinfolength),sizeof(uint16_t));
				memcpy(&(lbtemp.listBuff[49]),&(statusRspMsg.hostport),sizeof(uint16_t));
				memcpy(&(lbtemp.listBuff[51]),statusRspMsg.hostname,statusRspMsg.hostinfolength-sizeof(uint16_t));
				memcpy(&(lbtemp.listBuff[51+statusRspMsg.hostinfolength-sizeof(uint16_t)]),statusRspMsg.data,dataLen - 24 - statusRspMsg.hostinfolength + sizeof(uint16_t));
				lbtemp.datalength = 27+dataLen;
				lbtemp.portNumber = eventReq.portNumber;
				pthread_mutex_lock(&writeMapMutex);
				writeListData listDataStr = writeMap[eventReq.portNumber];
				//put them in the writeQueue.
				listDataStr.response.push_back(lbtemp);
				writeMap[eventReq.portNumber] = listDataStr;
				pthread_mutex_unlock(&writeMapMutex);
			}
		}
	}
	
	//Process the status response accordingly
	if((uint8_t)msgRespHdr.msgtype == 0xAB)
	{
		if(printFlag==1)
		{
			cout<<"Got Status Response msg from - "<<eventReq.portNumber<<endl;
		}
		char UOIDtemp[20];
		memset(UOIDtemp, '\0', 20);
		memcpy(&UOIDtemp, &(eventReq.listBuff[27]), 20);
		//If I am the request initiator then store the response onto a .out file
		bool match = false;
		for(itR=statusResponseList.begin(); itR!=statusResponseList.end(); itR++)
		{
			statusSTR temp = *itR;
			if(memcmp(UOIDtemp,temp.uoid,20)==0)
			{
				if(printFlag==1)
				{
					cout<<"MATCH FOUND"<<endl;
				}
				match = true;
				memcpy(&statType, &(temp.statusType), sizeof(uint8_t));
				break;
			}
		}
		if(match==true)
		{
			if(statType == (uint8_t)0x01)
			{
				if(printFlag == 1)
				{	
					cout<<"neigh status"<<endl;
				}
				status_response_msg tempMsg;
				memcpy(&(tempMsg.uoid), &(eventReq.listBuff[27]), 20);
				memcpy(&(tempMsg.hostinfolength), &(eventReq.listBuff[47]), sizeof(uint16_t));
				memcpy(&(tempMsg.hostport), &(eventReq.listBuff[49]), sizeof(uint16_t));
				memset(tempMsg.hostname, '\0', 512);
				memcpy(&(tempMsg.hostname), &(eventReq.listBuff[51]), tempMsg.hostinfolength-sizeof(uint16_t));
				memset(tempMsg.data,'\0', 8192);
				memcpy(&(tempMsg.data), &(eventReq.listBuff[51+(tempMsg.hostinfolength-sizeof(uint16_t))]), msgRespHdr.datalength-24-tempMsg.hostinfolength+sizeof(uint16_t));
				if(printFlag==1)
				{
					cout<<"Got from "<<eventReq.portNumber<<endl;
				}
				for(int i=0; i<20; i++)
				{
					if(printFlag==1)
						{
							printf("%02x", (uint8_t)(tempMsg.uoid[i]));
						}
				}
				if(printFlag==1)
				{
					cout<<endl;
					printf("%d\n", tempMsg.hostinfolength);
					printf("%d\n", tempMsg.hostport);
					cout<<tempMsg.hostname<<endl;
				}
				int tempIndex = 0;
				uint32_t tempLeng = -1;
				while(1)
				{
					memcpy(&tempLeng, &tempMsg.data[tempIndex], sizeof(uint32_t));
					if(printFlag==1)
					{
						printf("temp leng - %d\n",tempLeng);
						printf("temp index - %d\n", tempIndex);
					}
					uint16_t datasport;
					char datashost[512];
					if(tempLeng != 0)
					{
						memset(datashost, '\0', 512);
						memcpy(&datasport, &tempMsg.data[tempIndex+sizeof(uint32_t)], sizeof(uint16_t));
						memcpy(&datashost, &tempMsg.data[tempIndex+sizeof(uint32_t)+sizeof(uint16_t)], tempLeng-(sizeof(uint32_t)+sizeof(uint16_t)));
						pthread_mutex_lock(&statusRespLock);
						statusResp.insert(pair<int, int>(tempMsg.hostport, datasport));
						pthread_mutex_unlock(&statusRespLock);
						if(printFlag==1)
						{
							printf("%d\n", datasport);
							printf("%s\n",datashost);
						}
					}
					else
					{
						memset(datashost, '\0', 512);
						memcpy(&datasport, &tempMsg.data[tempIndex+sizeof(uint32_t)], sizeof(uint16_t));
						memcpy(&datashost, &tempMsg.data[tempIndex+sizeof(uint32_t)+sizeof(uint16_t)], msgRespHdr.datalength-24-tempIndex-(sizeof(uint32_t)+sizeof(uint16_t))-strlen(tempMsg.hostname));
						pthread_mutex_lock(&statusRespLock);
						statusResp.insert(pair<int, int>(tempMsg.hostport, datasport));
						pthread_mutex_unlock(&statusRespLock);
						if(printFlag==1)
						{
							printf("%d\n", datasport);
							printf("%s\n",datashost);
						}
						break;
					}
					tempIndex = tempIndex + tempLeng + sizeof(uint32_t);
				}
			}
			else if(statType == (uint8_t)0x02)
			{
				if(printFlag == 1)
				{	
					cout<<"Files status"<<endl;
				}
				status_response_msg tempMsg;
				memcpy(&(tempMsg.uoid), &(eventReq.listBuff[27]), 20);
				memcpy(&(tempMsg.hostinfolength), &(eventReq.listBuff[47]), sizeof(uint16_t));
				memcpy(&(tempMsg.hostport), &(eventReq.listBuff[49]), sizeof(uint16_t));
				memset(tempMsg.hostname, '\0', 512);
				memcpy(&(tempMsg.hostname), &(eventReq.listBuff[51]), tempMsg.hostinfolength-sizeof(uint16_t));
				if(msgRespHdr.datalength == (24+strlen(tempMsg.hostname)))
				{
					char path[256];
					memset(path, '\0', 256);
					strcpy(path, statusFile);
					FILE *filePt;
					filePt = fopen(path, "a");
					char line1[256];
					memset(line1, '\0', 256);
					sprintf(line1, "%s%s%d%s", tempMsg.hostname, ":", tempMsg.hostport, " has no files\n");
					fputs(line1, filePt);
					fclose(filePt);
				}
				else
				{
					memset(tempMsg.data,'\0', 8192);
					memcpy(&(tempMsg.data), &(eventReq.listBuff[51+(tempMsg.hostinfolength-sizeof(uint16_t))]), msgRespHdr.datalength-24-tempMsg.hostinfolength+sizeof(uint16_t));
					int tempIndex = 0;
					uint32_t tempLeng = -1;
					while(1)
					{
						memcpy(&tempLeng, &tempMsg.data[tempIndex], sizeof(uint32_t));
						if(tempLeng != 0)
						{
							pthread_mutex_lock(&storeFilesLock);
							statusFilesData statData;
							memset(statData.data, '\0', 8192);
							memcpy(&(statData.data), &tempMsg.data[tempIndex + sizeof(uint32_t)], tempLeng);
							memcpy(&(statData.hostport), &(tempMsg.hostport), sizeof(uint16_t));
							memset(statData.hostname, '\0', 128);
							memcpy(&(statData.hostname), &(tempMsg.hostname), strlen(tempMsg.hostname));
							statusFilesDataList.push_back(statData);
							pthread_mutex_unlock(&storeFilesLock);
							if(printFlag == 1)
							{
								cout<<statData.hostport<<endl;
								cout<<statData.data<<endl;
							}
						}
						else
						{
							pthread_mutex_lock(&storeFilesLock);
							statusFilesData statData;
							memset(statData.data, '\0', 8192);
							memcpy(&(statData.data), &tempMsg.data[tempIndex + sizeof(uint32_t)], msgRespHdr.datalength-24-(tempMsg.hostinfolength-sizeof(uint16_t))-tempIndex-sizeof(uint32_t));
							memcpy(&(statData.hostport), &(tempMsg.hostport), sizeof(uint16_t));
							memset(statData.hostname, '\0', 128);
							memcpy(&(statData.hostname), &(tempMsg.hostname), strlen(tempMsg.hostname));
							statusFilesDataList.push_back(statData);
							pthread_mutex_unlock(&storeFilesLock);
							if(printFlag == 1)
							{
								cout<<statData.hostport<<endl;
								cout<<statData.data<<endl;
							}
							break;
						}
						tempIndex = tempIndex + tempLeng + sizeof(uint32_t);
					}
				}
			}
		}
		else
		{
			//If I am not the request initiator then send it to the person who requested/forwarded the request
			int testUOID = findInUOIDList(msgRespHdr.uoid);
			if(testUOID == 1)
			{
				
			}
			else
			{
				//Store the request in the uoid queue.
				uoidStr tempUOIDStr;
				struct timeval time1;
				gettimeofday(&time1, NULL);
				tempUOIDStr.timeOfTheDay = (double)(time1.tv_sec * (1e+3) + (double)time1.tv_usec/1000);
				tempUOIDStr.msgLifeTime = initMap["MsgLifetime"].asDouble;
				memcpy(tempUOIDStr.msgUOID, msgRespHdr.uoid, 20);
				pthread_mutex_lock(&uoidlock);
				myUOIDList.push_back(tempUOIDStr);
				pthread_mutex_unlock(&uoidlock);
				//If TTL is 1 then do not forward
				if(msgRespHdr.timetolive==1)
				{
			
				}
				else
				{
					//Sending the response to the forwarder
					listBuffStruct lbtemp;
					int tempTTL = msgRespHdr.timetolive;
					if(printFlag==1)
					{
						cout<<"TTL - "<<tempTTL<<endl;
					}
					tempTTL--;
					msgRespHdr.timetolive = tempTTL;
					memset(&lbtemp.listBuff, '\0', 8192);
					fillMsgHdrBuffer(msgRespHdr, lbtemp.listBuff);
					memcpy(&(lbtemp.listBuff[27]),&(eventReq.listBuff[27]),msgRespHdr.datalength);
					lbtemp.datalength = eventReq.datalength;
					lbtemp.portNumber = eventReq.portNumber;
					lbtemp.logtype=2;
					pthread_mutex_lock(&writeMapMutex);
					
					for(itF=myFromUOIDList.begin(); itF!=myFromUOIDList.end(); itF++)
					{
						fromUOIDStr temp = *itF;
						if(memcmp(UOIDtemp,temp.uoid,20)==0)
						{
							writeListData listDataStr = writeMap[temp.portnumber];
							listDataStr.response.push_back(lbtemp);
							writeMap[temp.portnumber] = listDataStr;
							break;
						}
					}
					pthread_mutex_unlock(&writeMapMutex);
				}
			}
		}
	}
}
